#include "almacenamiento.h"

extern pthread_mutex_t mutex_cache;
t_cache_global cacheGlobal;



uint32_t *obtener_fat(t_fs *fs, uint16_t SecRes, uint32_t tamFat, int32_t Sock) {


	uint32_t i = SecRes / 2, bloqRes;

	char *bloque;
	char *tablaFat = (char *)calloc(1,tamFat);
	size_t j = 0;
	uint32_t bloquesFat = tamFat / TAM_BLOQUE;
	bloqRes = i;

	while (i < (bloquesFat + bloqRes)) { //mientras q la cant leida sea menor q el tamanio de la fat en bloqs
		bloque = pedir_bloque(i, Sock);
		memcpy(tablaFat + j, bloque, TAM_BLOQUE);

		j = j + TAM_BLOQUE; //sumo el desplazamiento de bloque leido

		i++;
	}

	return (uint32_t*)tablaFat;
}

char *pedir_bloque(uint32_t bloque,int32_t sock){
	uint32_t sector1,sector2;
	char cadena1[20],cadena2[20],respuesta1[1100],respuesta2[1100];
	char *respuesta;

	memset(respuesta1,'\0',1100);
	memset(respuesta2,'\0',1100);

	sector1=(bloque*2) ; /** El Bloque cero tiene sector 0 y 1 , el bloque uno sectores 2 y 3 etc..**/
	sector2=(bloque*2)+1;

	sprintf(cadena1,"read(%d)",sector1);
	sprintf(cadena2,"read(%d)",sector2);
	respuesta = ComunicacionBlock(sock,cadena1,cadena2);

	//printf("Se realizo la lectura del Sector correctamente.\n");

	return respuesta;
}

void solo_pedir_bloque(uint32_t bloque,int32_t sock, char *idDescriptor1, char* idDescriptor2 ){
	uint32_t sector1,sector2;
	char cadena1[20],cadena2[20];
	TMensaje Mensaje1,Mensaje2;
	time_t tActual;
	char id1[16];
	char id2[16];
	struct tm* tiempoGenerico;
	char cad[50];


	sector1=(bloque*2) ; /** El Bloque cero tiene sector 0 y 1 , el bloque uno sectores 2 y 3 etc..**/
	sector2=(bloque*2)+1;

	sprintf(cadena1,"read(%d)",sector1);
	sprintf(cadena2,"read(%d)",sector2);


	tActual = time(NULL);
	tiempoGenerico = localtime(&tActual);
	strftime(cad, 50, "%H%M%S", tiempoGenerico);

	//Mensaje1=(TMensaje*)calloc(1,sizeof(TMensaje));

	sprintf(Mensaje1.idDescriptor,"%s%010i",cad,rand());
	memcpy(id1,Mensaje1.idDescriptor,16);
	strcpy(Mensaje1.payload,cadena1);
	Mensaje1.payloadLength= strlen(Mensaje1.payload);
	Mensaje1.payloadDescriptor = '0';
	send(sock,(void*)&Mensaje1,sizeof(struct TMensaje),0);


	tActual = time(NULL);
	tiempoGenerico = localtime(&tActual);
	strftime(cad, 50, "%H%M%S", tiempoGenerico);

	//Mensaje2=(TMensaje*)calloc(1,sizeof(TMensaje));

	sprintf(Mensaje2.idDescriptor,"%s%010i",cad,rand());
	memcpy(id2,Mensaje2.idDescriptor,16);
	strcpy(Mensaje2.payload,cadena2);
	Mensaje2.payloadLength= strlen(Mensaje2.payload);
	Mensaje2.payloadDescriptor = '0';
	send(sock,(void*)&Mensaje2,sizeof(struct TMensaje),0);


	memcpy(idDescriptor1 , id1, 16);
	memcpy(idDescriptor2 , id2, 16);


}

void solo_recibir_bloques(char16 idDescVec[8], int32_t Sock, int32_t cantBloques, char* bloques_pedidos){
	int32_t bytesRec= 1;
	int32_t j = 0, i;
	TMensaje Mensaje;
	
	
	for(i=0;i< cantBloques ;i++){
	
		//recibo 
		while(bytesRec<sizeof(TMensaje)){
			bytesRec=recv(Sock, (void*)&Mensaje, sizeof(struct TMensaje), MSG_WAITALL);
			if(bytesRec==0 || bytesRec ==-1){
				printf("error recv");
				exit(EXIT_FAILURE);
			}
		}	
		while( memcmp(Mensaje.idDescriptor, idDescVec[j].idDesc, 16))
				j++;
		
		memcpy(bloques_pedidos + (512*j), Mensaje.payload,512);
		
		bytesRec=1;
		j = 0;
		while(bytesRec<sizeof(TMensaje)){
			bytesRec=recv(Sock, (void*)&Mensaje, sizeof(struct TMensaje), MSG_WAITALL);
			if(bytesRec==0 || bytesRec ==-1){
				printf("error recv");
				exit(EXIT_FAILURE);
			}
		}	
		while( memcmp(Mensaje.idDescriptor, idDescVec[j].idDesc, 16))
				j++;
		memcpy(bloques_pedidos + (512*j), Mensaje.payload,512);
		j = 0;
		bytesRec = 1;
	}
}

char *obtener_cluster(t_fs *fs, uint32_t NumCluster,uint32_t Sock){

    uint32_t numSec, numBloq, pos = 0;
	int32_t cant_bloques;
	char *bloques_pedidos;

    char *cluster=(char*)calloc(1,TAM_CLUSTER);
    int32_t i;
    char idDescriptor1[16];
    char idDescriptor2[16];
    char16 idDescVec[8];


    numSec = obt_direccion_cluster(fs, NumCluster);
	numBloq = numSec/2;
	
    for(i = 0 ; i < (fs->bootsector.sectorPerCluster/2) ; i++){
		
		//pido un bloque
    	solo_pedir_bloque(numBloq,Sock, idDescriptor1, idDescriptor2);
		
		//me guardo los idDescriptor
		memcpy(idDescVec[pos].idDesc, idDescriptor1, 16);
    	memcpy(idDescVec[pos + 1].idDesc, idDescriptor2, 16);
        
		numBloq ++;
        pos = pos +2;
     
    }
	cant_bloques = 4;
	
	bloques_pedidos = (char*)calloc(1,(cant_bloques*1024));
	
	solo_recibir_bloques(idDescVec, Sock, cant_bloques, bloques_pedidos);
	
	memcpy(cluster, bloques_pedidos,TAM_CLUSTER);
	
	free(bloques_pedidos);
	
	return cluster;
}

int32_t escribir_cluster(t_fs *fs, int32_t numClus,char* cluster, int32_t Sock){

	int32_t i=0,numSec=0,numBloque=0;

	char bloque[1024];



	numSec = obt_direccion_cluster(fs, numClus);
	numBloque = (numSec)/2;
	while(i <(fs->bootsector.sectorPerCluster)/2){
		memset(bloque,'\0',1024);
		memcpy(bloque,cluster+(i*1024),1024);
		escribir_bloque(numBloque,bloque, Sock);

		numBloque =numBloque + 1;
		i++;

	}

	return 1;
}

int32_t escribir_bloque(int32_t numBloque,char *bloque, int32_t Sock){
	int32_t Res=0;
	char respuesta[1100];
	char Sec[512];
	int32_t numSec = (numBloque*2);
	memcpy(Sec,bloque,512);
	Res=Comunicacion(Sock,'1',numSec,Sec,respuesta);
	/*if(Res)
		printf("Se escribio correctamente el Sector\n");
	else{
		printf("ERROR: No se pudo escribir Sector\n");

		return 0;
		*/


	memset(Sec,'\0',512);
	memcpy(Sec,bloque+512,512);
	Res=0;
	numSec++;
	Res=Comunicacion(Sock,'1',numSec,Sec,respuesta);
	/*if(Res)
		printf("Se escribio correctamente el Sector\n");
	else{
		printf("ERROR: No se pudo escribir Sector\n");
		return 0;
	}
	*/
	return 1;

}

char *leer_cluster_cache_archivo(t_fs *fs, t_elemento *arch,int32_t numCluster,int32_t Sock){
	char *cluster = NULL;
	int32_t escrito = 0;
	t_cache *nodoCache;
	extern int32_t tamCache;
	extern int32_t tamCacheG;


	if((tamCache != 0) && esta_en_cache(arch->cache, numCluster, &nodoCache)){
		return nodoCache->cluster;
	}
	else{
		if ((tamCacheG != 0) && esta_en_cache_global(numCluster)){
			actualizar_cache(fs, arch, cacheGlobal.numClus, cacheGlobal.cluster, Sock, escrito);
			cluster = (char*)calloc(1,TAM_CLUSTER);
			memcpy(cluster,cacheGlobal.cluster,TAM_CLUSTER);
			return cluster;
		}
		cluster = obtener_cluster(fs, numCluster,Sock);
		actualizar_cache(fs, arch, numCluster, cluster, Sock, escrito);
		return cluster;
	}

}

void escribir_cluster_cache_archivo(t_fs *fs, t_elemento *arch,int32_t numCluster,char *clusterNuevo,int32_t Sock){
	int32_t escrito = 1;
	t_cache *nodoCache;


	if(esta_en_cache(arch->cache, numCluster, &nodoCache )){

		memcpy(nodoCache->cluster,clusterNuevo,TAM_CLUSTER);
		nodoCache->escrito = 1;
		nodoCache->cont ++;

	}
	else{
		actualizar_cache(fs, arch, numCluster, clusterNuevo, Sock, escrito);

	}

}

int32_t esta_en_cache(t_cache *cache,uint32_t numClus, t_cache **cacheActual){
	extern int32_t tamCache;

	pthread_mutex_lock(&mutex_cache);
	if (tamCache == 0){
		pthread_mutex_unlock(&mutex_cache);
		return 0;
	}
	if (cache == NULL){
		pthread_mutex_unlock(&mutex_cache);
		return 0;
	}
	t_cache *punt = cache;

	while(punt->numClus != numClus){
		punt = (t_cache*)punt->next;
		if(punt == NULL)
			break;
	}

	if( punt == NULL){
		pthread_mutex_unlock(&mutex_cache);
		return 0;
	}
	else{
		*cacheActual = punt;
		(*cacheActual)->cont ++;
		pthread_mutex_unlock(&mutex_cache);
		return 1;
	}
}

void actualizar_cache(t_fs *fs ,t_elemento *arch,uint32_t numClus,char *cluster, int32_t Sock, int32_t escrito){


	extern int32_t tamCache;
	int32_t cantNodosCache = 1;
	t_cache *puntAnt = NULL;
	t_cache * nuevoElemCache,*punt = NULL;
	extern int32_t tamCacheG;

	t_cache *cache = arch->cache;
	if(tamCache != 0){
		pthread_mutex_lock(&mutex_cache);

		if (cache == NULL){
			nuevoElemCache = (t_cache*)calloc(1,sizeof(t_cache));
			nuevoElemCache->cluster = (char *)calloc(1,TAM_CLUSTER);
			memcpy(nuevoElemCache->cluster, cluster,TAM_CLUSTER);
			nuevoElemCache->cont = 0;
			nuevoElemCache->escrito= escrito;
			nuevoElemCache->numClus = numClus;
			nuevoElemCache->next = NULL;


			arch->cache = nuevoElemCache;
			pthread_mutex_unlock(&mutex_cache);
			return;
		}


		punt = (t_cache*)cache->next;
		puntAnt = cache;
		while(punt != NULL){
			cantNodosCache ++;
			puntAnt = punt;
			punt = (t_cache*)punt->next;
		}
		if (cantNodosCache < tamCache/4){

			t_cache * nuevoElemCache = (t_cache*)calloc(1,sizeof(t_cache));
			nuevoElemCache->cluster = (char *)calloc(1,TAM_CLUSTER);
			memcpy(nuevoElemCache->cluster, cluster,TAM_CLUSTER);
			nuevoElemCache->cont = 0;
			nuevoElemCache->escrito= escrito;
			nuevoElemCache->numClus = numClus;
			nuevoElemCache->next = NULL;

			puntAnt->next = (struct t_cache*)nuevoElemCache;


		}
		else{
			reemplazarMenor(fs,cache, numClus,cluster,escrito, Sock);
		}
		pthread_mutex_unlock(&mutex_cache);
	}
	else{
		if((tamCacheG != 0) && esta_en_cache_global(numClus)){
			memcpy(cacheGlobal.cluster,cluster,TAM_CLUSTER);
			cacheGlobal.escrito=escrito;
		}
		else
			if (tamCacheG != 0)
				actualizar_cache_global( fs, numClus, cluster,  Sock,  escrito);

		//pthread_mutex_unlock(&mutex_cache);
	}
}



void reemplazarMenor(t_fs *fs, t_cache *cache,int32_t numClus,char* cluster,int32_t escrito, int32_t Sock){

	t_cache*punt = NULL, *puntAReemp = NULL, *puntAnt = NULL;
	int32_t i = 5000;


	punt = cache;

	while(punt != NULL){

		if(punt->cont < i){
			puntAReemp = punt;
			i = punt->cont;
		}
		puntAnt = punt;
		punt = (t_cache*)punt->next;
	}
	if (puntAnt->escrito){
		escribir_cluster(fs,numClus,cluster,Sock);
	}

	memcpy(puntAnt->cluster, cluster,TAM_CLUSTER);
	puntAnt->cont = 0;
	puntAnt->escrito= escrito;
	puntAnt->numClus = numClus;


}

void flush(t_fs *fs, t_elemento *elemento, int32_t Sock){
	t_cache * punt;
	extern int32_t tamCacheG;

	if(tamCacheG !=0){
		punt = elemento->cache;
		while (punt != NULL){
			if (punt->escrito == 1){
				escribir_cluster(fs,punt->numClus,punt->cluster,Sock);
				punt->escrito = 0;
			}
			punt = (t_cache*)punt->next;

		}
	}
}

void flush_cache_global(t_fs *fs,int32_t Sock){

	if (cacheGlobal.escrito == 1){
		escribir_cluster(fs,cacheGlobal.numClus,cacheGlobal.cluster,Sock);
		cacheGlobal.escrito = 0;
	}


}



int32_t esta_en_cache_global(uint32_t numClus){

	if (cacheGlobal.numClus == numClus){
		return 1;
	}
	return 0;


}
void actualizar_cache_global(t_fs* fs, uint32_t numClus,char *cluster, int32_t Sock, int32_t escrito){

	if (cacheGlobal.escrito == 1){
		escribir_cluster(fs,cacheGlobal.numClus,cacheGlobal.cluster,Sock);
	}
	memcpy(cacheGlobal.cluster, cluster, TAM_CLUSTER);
	cacheGlobal.numClus = numClus;
	cacheGlobal.escrito = escrito;

}


char *obtener_cluster_cacheG(t_fs *fs,uint32_t numClus, int32_t Sock){
	char *clus;
	int32_t escrito = 0;

	if (esta_en_cache_global(numClus))
		return cacheGlobal.cluster;

	clus = obtener_cluster(fs,numClus,Sock);
	actualizar_cache_global(fs,numClus,clus,Sock, escrito);
	return clus;
}

void escribir_cluster_cacheG(t_fs *fs, int32_t numClus,char *clusterNuevo,int32_t Sock){
	int32_t escrito = 1;
	if (esta_en_cache_global(numClus)){

		memcpy(cacheGlobal.cluster, clusterNuevo, TAM_CLUSTER);
		cacheGlobal.escrito = 1;
	}
	else{
		actualizar_cache_global(fs,numClus,clusterNuevo,Sock, escrito);
	}
}
